﻿using System.IO;
using System.Text;
using System.Web;
using System.Security.Cryptography;
using BaseClasses.Resources;

//SymmCrypto is a wrapper of System.Security.Cryptography.SymmetricAlgorithm classes
//and simplifies the interface. It supports customized SymmetricAlgorithm as well.
//Original Code from Frank Fang
//Revised by Jerome Howard to remove Bad Data errors, create seperate CryptoIV and
//use the maximum legal keysize for each encryption algorithm

namespace EnGasAdoAPP.Data
{
    public class Crypto
    {
        //256 Bit IV Key that is truncated when a smaller keys are required
        private byte [] _bytIV = {78, 90, 23, 7, 54, 109, 34, 231, 90, 66, 109, 185, 228, 143, 89, 77, 190, 89, 103, 148, 54, 4, 98, 67, 243, 162, 68, 201, 73, 59, 184, 52 };
        private byte [] bytIV;
        //Supported .Net intrinsic SymmetricAlgorithm classes.
        public enum Providers {  DES, RC2, Rijndael }
        private SymmetricAlgorithm _CryptoService;

        public Crypto(): this(Crypto.Providers.DES)
        {
            // no additional work
        }

        //Constructor for using an intrinsic .Net SymmetricAlgorithm class.
        public Crypto(Providers NetSelected)
        {
            switch (NetSelected)
            {
                case Providers.DES:
                    _CryptoService = new DESCryptoServiceProvider();
                    break;
                case Providers.RC2:
                    _CryptoService = new RC2CryptoServiceProvider();
                    break;
                case Providers.Rijndael:
                    _CryptoService = new RijndaelManaged();
                    break;
            }
        }
        //Constructor for using a customized SymmetricAlgorithm class.
        public Crypto(SymmetricAlgorithm ServiceProvider)
        {
            _CryptoService = ServiceProvider;
        }
        //Depending on the legal key size limitations of a specific CryptoService provider
        //and length of the private key provided, padding the secret key with a character
        //or triming it to meet the legal size of the algorithm. 
        private byte[] GetLegalKey(string Key)
        {
            //key sizes are in bits
            string sTemp;
            int dValue;
            if (_CryptoService.LegalKeySizes.Length > 0)
            {
                int maxSize = _CryptoService.LegalKeySizes[0].MaxSize;
                dValue = (int)(maxSize / 8);
                if (Key.Length * 8 > maxSize) { sTemp = Key.Substring(0, dValue); }
                else
                {
                    int moreSize = _CryptoService.LegalKeySizes[0].MinSize;
                    while (Key.Length * 8 > moreSize) { moreSize += _CryptoService.LegalKeySizes[0].SkipSize; }
                    sTemp = Key.PadRight(dValue, "X"[0]);
                }
            }
            else {                sTemp=Key; }
            if (_CryptoService.LegalBlockSizes.Length > 0)
                {
                int maxSize = _CryptoService.LegalBlockSizes[0].MaxSize;
                dValue = (int)(maxSize / 8);
                int byteIVsize;
                if(sTemp.Length * 8 > maxSize) byteIVsize = dValue; 
                else byteIVsize = sTemp.Length;
                this.bytIV = new byte[byteIVsize];
                for (int i = 0; (i < this._bytIV.Length) && (i < byteIVsize); i++ ) this.bytIV[i] = this._bytIV[i];
            }

            // not finished
            //convert the secret key to byte array 
            return ASCIIEncoding.ASCII.GetBytes(sTemp);
        }

        public string Encrypt(string Source)
        {
            if (Source == null) { return ""; }
            string s = "";
            try
            {
                s = this.Encrypt(Source, this.GetCryptoKey(), false);
                s = System.Web.HttpUtility.UrlEncode(s);
            }
            catch { s = Source; }
            return s;
        }

        public string Encrypt(string Source, bool includeSession)
        {
            if (Source == null) { return ""; }
            string s = "";
            try
            {
                if (includeSession)
                {
                    s = this.Encrypt(Source, this.GetCryptoKey(), false);
                    s = System.Web.HttpUtility.UrlEncode(s);
                }
                else
                {
                    s = this.Encrypt(Source, this.GetCryptoKeyWithoutSessionVariable(), false);
                    s = System.Web.HttpUtility.UrlEncode(s);
                }
            }
            catch { s = Source; }
            return s;
        }

        public string Encrypt(string Source, string Key, bool Encoded)
        {
            return this.Encrypt(Source, Key, System.Text.Encoding.ASCII, Encoded);
        }

        public string Encrypt(string Source, string Key, System.Text.Encoding Encoding, bool Encoded)
        {
            if (Source == null || Source.Trim().Length == 0)
                return Source;


            if (Encoded)
            {
                Source = System.Web.HttpUtility.UrlEncode(Source);
            }
            byte[] bytIn;
            if (Encoding == System.Text.Encoding.ASCII)
            {
                bytIn = System.Text.ASCIIEncoding.GetEncoding(System.Globalization.CultureInfo.CurrentUICulture.TextInfo.ANSICodePage).GetBytes(Source);
            }
            else if (Encoding == System.Text.Encoding.Unicode)
            {
                bytIn = System.Text.UnicodeEncoding.GetEncoding(System.Globalization.CultureInfo.CurrentUICulture.TextInfo.ANSICodePage).GetBytes(Source);
            }
            else
            {
                bytIn = System.Text.ASCIIEncoding.GetEncoding(System.Globalization.CultureInfo.CurrentUICulture.TextInfo.ANSICodePage).GetBytes(Source);
            }
            //byte[] bytIn = System.Text.ASCIIEncoding.ASCII.GetBytes(Source); 
            MemoryStream ms = new MemoryStream();


            //set the keys 
            _CryptoService.Key = GetLegalKey(Key);
            if (this.bytIV == null) _CryptoService.IV = _bytIV;
            else _CryptoService.IV = bytIV;

            //create an Encryptor from the Provider Service instance 
            ICryptoTransform encrypto = _CryptoService.CreateEncryptor();

            //create Crypto Stream that transforms a stream using the encryption 
            CryptoStream cs = new CryptoStream(ms, encrypto, CryptoStreamMode.Write);

            //write out encrypted content into MemoryStream 
            cs.Write(bytIn, 0, bytIn.Length);
            cs.FlushFinalBlock();
            cs.Close();
            byte[] bytOut = ms.ToArray();
            ms.Close();

            return System.Convert.ToBase64String(bytOut);
            //convert into Base64 so that the result can be used in xml 
        }

        
        public string Decrypt(string Source)
        {

            string s = "";
            try
            {
                // First try decrypting as is.
                s = this.Decrypt(Source, this.GetCryptoKey(), false);
                s = System.Web.HttpUtility.UrlDecode(s);
            }
            catch
            {
                try
                {
                    // If the first try of decrypting does not work, then
                    // URL decode it and try again.  This is to ensure that if
                    // the encrypted key is passed through the URL, it needs to
                    // be decoded first.
                    Source = System.Web.HttpUtility.UrlDecode(Source);
                    s = this.Decrypt(Source, this.GetCryptoKey(), false);
                }
                catch
                {
                    throw new System.UriFormatException(RU.GetErrMsg(RU.ErrRes.GetRecords));
                }


            }
            if ((Source != null && Source.Trim() != "") && (s == null || s.Trim() == ""))
                throw new System.UriFormatException(RU.GetErrMsg(RU.ErrRes.GetRecords));

            return s;
        }

        public string Decrypt(string Source, bool includeSession)
        {

            string s = "";
            try
            {
                // First try decrypting as is.
                if (includeSession)
                {
                    s = this.Decrypt(Source, this.GetCryptoKey(), false);
                }
                else
                {
                    s = this.Decrypt(Source, this.GetCryptoKeyWithoutSessionVariable(), false);
                }
                s = System.Web.HttpUtility.UrlDecode(s);
            }
            catch
            {
                try
                {
                    // If the first try of decrypting does not work, then
                    // URL decode it and try again.  This is to ensure that if
                    // the encrypted key is passed through the URL, it needs to
                    // be decoded first.
                    Source = System.Web.HttpUtility.UrlDecode(Source);
                    if (includeSession)
                    {
                        s = this.Decrypt(Source, this.GetCryptoKey(), false);
                    }
                    else
                    {
                        s = this.Decrypt(Source, this.GetCryptoKeyWithoutSessionVariable(), false);
                    }
                }
                catch
                {
                    throw new System.UriFormatException(RU.GetErrMsg(RU.ErrRes.GetRecords));
                }
            }

            if ((Source != null && Source.Trim() != "") && (s == null || s.Trim() == ""))
                throw new System.UriFormatException(RU.GetErrMsg(RU.ErrRes.GetRecords));

            return s;
        }

        public string Decrypt(string Source, string Key, bool Encoded)
        {
            return this.Decrypt(Source, Key, System.Text.Encoding.ASCII, Encoded);
        }

        public string Decrypt(string Source, string Key, System.Text.Encoding Encoding, bool Encoded)
        {
            if (Source == null || Source.Trim().Length == 0)
                return Source;

            //convert from Base64 to binary 
            byte[] bytIn = null;
            MemoryStream ms = null;
            try
            {
                bytIn = System.Convert.FromBase64String(Source);
                ms = new MemoryStream(bytIn);
            }
            catch
            {
                bytIn = System.Convert.FromBase64String(Source.Replace(" ", "+"));
                ms = new MemoryStream(bytIn);
            }

            byte[] bytKey = GetLegalKey(Key);
            byte[] bytTemp = new byte[bytIn.Length + 1];

            //set the private key 
            _CryptoService.Key = bytKey;
            if (this.bytIV == null) _CryptoService.IV = _bytIV;
            else _CryptoService.IV = bytIV;

            //create a Decryptor from the Provider Service instance 
            ICryptoTransform encrypto = _CryptoService.CreateDecryptor();

            //create Crypto Stream that transforms a stream using the decryption 
            CryptoStream cs = new CryptoStream(ms, encrypto, CryptoStreamMode.Read);
            string output = "";
            try
            {
                //read out the result from the Crypto Stream 
                StreamReader sr;
                if (Encoding == System.Text.Encoding.ASCII)
                {
                    sr = new StreamReader(cs, System.Text.ASCIIEncoding.GetEncoding(System.Globalization.CultureInfo.CurrentUICulture.TextInfo.ANSICodePage));
                }
                else if (Encoding == System.Text.Encoding.Unicode)
                {
                    sr = new StreamReader(cs, System.Text.UnicodeEncoding .GetEncoding(System.Globalization.CultureInfo.CurrentUICulture.TextInfo.ANSICodePage));
                }
                else
                {
                    sr = new StreamReader(cs, System.Text.ASCIIEncoding.GetEncoding(System.Globalization.CultureInfo.CurrentUICulture.TextInfo.ANSICodePage));
                }
                //StreamReader sr = new StreamReader(cs, System.Text.UnicodeEncoding.Unicode); 

                output = sr.ReadToEnd();
                sr.Close();
                ms.Close();
                cs.Close();
            }
            catch
            {
                bytIn = System.Convert.FromBase64String(Source.Replace(" ", "+"));
                ms = new MemoryStream(bytIn);
                bytKey = GetLegalKey(Key);

                //set the private key
                _CryptoService.Key = bytKey;
                _CryptoService.IV = bytIV;
                encrypto = _CryptoService.CreateDecryptor();

                try
                {
                    //create Crypto Stream that transforms a stream using the decryption
                    cs = new CryptoStream(ms, encrypto, CryptoStreamMode.Read);
                    StreamReader sr = null;
                    if (Encoding.EncodingName == System.Text.Encoding.ASCII.EncodingName)
                    {
                        sr = new StreamReader(cs, System.Text.ASCIIEncoding.GetEncoding(System.Globalization.CultureInfo.CurrentUICulture.TextInfo.ANSICodePage));
                    }
                    else if (Encoding.EncodingName == System.Text.Encoding.Unicode.EncodingName)
                    {
                        sr = new StreamReader(cs, System.Text.UnicodeEncoding.GetEncoding(System.Globalization.CultureInfo.CurrentUICulture.TextInfo.ANSICodePage));
                    }
                    else
                    {
                        sr = new StreamReader(cs, System.Text.ASCIIEncoding.GetEncoding(System.Globalization.CultureInfo.CurrentUICulture.TextInfo.ANSICodePage));
                    }
                    output = sr.ReadToEnd();
                    sr.Close();
                    ms.Close();
                    cs.Close();
                }
                catch
                {
                }
            }
            if (Encoded)
            {
                output = System.Web.HttpUtility.UrlDecode(output);
            }
            return output;
        } 

        // The URLEncryptionKey is specified in the web.config.  The rightmost six characters of the current
        // Session Id are concatenated with the URLEncryptionKey to provide added protection.  You can change
        // this to anything you like by changing this function for the application.
        // This function is private and not overridable because each page cannot have its own key - it must
        // be common across the entire application.
        private string GetCryptoKey()
        {
            return System.Web.HttpContext.Current.Session.SessionID.Substring(0, 6) + BaseClasses.Configuration.ApplicationSettings.Current.URLEncryptionKey + System.Web.HttpContext.Current.Session.SessionID.Substring(System.Web.HttpContext.Current.Session.SessionID.Length - 6);
        }

        private string GetCryptoKeyWithoutSessionVariable()
        {
            string key = "ffx4ypamvvcs4knwbxxehl45";
            return key.Substring(0, 6) + BaseClasses.Configuration.ApplicationSettings.Current.URLEncryptionKey + key.Substring(key.Length - 6);
        }

    }
}